Public Class SelectStatement

    Public Columns As New ColumnList
    Public FromClause As New FromClause
    Public WhereClause As New WhereClause
    Public OrderClause As New orderClause
    Public GroupClause As New GroupByClause
    Public HavingClause As New HavingClause

    Private _RowLimit As Integer
    Public Property RowLimit() As Integer
        Get
            If _RowLimit <= 0 Then
                Return 10000
            Else
                Return _RowLimit
            End If
        End Get
        Set(ByVal value As Integer)
            _RowLimit = value
        End Set
    End Property
    Public Sub New()

    End Sub
    Public Sub New(ByVal nodes As Parser.SyntaxNode)
        Parse(nodes, String.Empty, 0)
    End Sub

    Private Function ParseColumnList(ByVal node As Parser.SyntaxNode, ByVal ruleName As String, ByVal list As ColumnList) As ColumnList
        Debug.Assert(node IsNot Nothing)
        Debug.Assert(node.GetType().ToString = GetType(Parser.NonTerminalNode).ToString)
        Dim root As Parser.NonTerminalNode = CType(node, Parser.NonTerminalNode)
        If list Is Nothing Then list = New ColumnList
        For idx As Integer = 0 To root.Count - 1
            Select Case root.Item(idx).GetType().ToString
                Case GetType(Parser.NonTerminalNode).ToString
                    Dim n As Parser.NonTerminalNode = CType(root.Item(idx), Parser.NonTerminalNode)
                    ruleName = n.RuleName
                    Select Case ruleName.ToUpper
                        Case "<COLUMN LIST>"
                            list = ParseColumnList(n, ruleName, list)
                        Case "<COLUMN ITEM>"
                            list.Add(ParseColumn(n, ruleName, Nothing))
                    End Select
                Case GetType(Parser.TerminalNode).ToString
                    Console.WriteLine("Terminal Node")
                    'Do Nothing
            End Select
        Next
        Return list
    End Function

    Private Function ParseColumns(ByVal node As Parser.SyntaxNode, ByVal ruleName As String, ByVal list As ColumnList) As ColumnList
        Debug.Assert(node IsNot Nothing)
        Debug.Assert(node.GetType.ToString() = GetType(Parser.NonTerminalNode).ToString)
        If list Is Nothing Then list = New ColumnList
        Dim root As Parser.NonTerminalNode = CType(node, Parser.NonTerminalNode)
        For idx As Integer = 0 To root.Count - 1
            Select Case root(idx).GetType().ToString
                Case GetType(Parser.NonTerminalNode).ToString
                    Dim n As Parser.NonTerminalNode = CType(root(idx), Parser.NonTerminalNode)
                    ruleName = n.RuleName
                    Select Case ruleName.ToUpper
                        Case "<COLUMN LIST>"
                            list = ParseColumnList(n, n.RuleName, New ColumnList)
                    End Select
                Case GetType(Parser.TerminalNode).ToString
                    Console.WriteLine("Terminal Node")
                    Dim t As Parser.TerminalNode = CType(root(idx), Parser.TerminalNode)
                    Select Case ruleName.ToUpper
                        Case "<COLUMNS>"
                            If t.Text = "*" Then
                                list.IsWildCard = True
                            End If

                    End Select
                    'Do Nothing
            End Select
        Next
        Return list
    End Function


    Private Function ParseColumn(ByVal node As Parser.SyntaxNode, ByVal ruleName As String, ByVal column As ColumnDefinition) As ColumnDefinition
        Select Case node.GetType().ToString
            Case GetType(Parser.NonTerminalNode).ToString
                Dim n As Parser.NonTerminalNode = CType(node, Parser.NonTerminalNode)
                ruleName = n.RuleName
                Select Case ruleName.ToUpper
                    Case "<COLUMN ITEM>", "<AGGREGATE>"
                        column = New ColumnDefinition
                End Select
                For idx As Integer = 0 To n.Count - 1
                    If TypeOf (n.Item(idx)) Is Parser.TerminalNode Then
                        column = ParseColumn(n.Item(idx), ruleName, column)
                    Else
                        column = ParseColumn(n.Item(idx), CType(n.Item(idx), Parser.NonTerminalNode).RuleName, column)
                    End If
                Next
            Case GetType(Parser.TerminalNode).ToString
                Dim t As Parser.TerminalNode = CType(node, Parser.TerminalNode)
                Select Case ruleName.ToUpper()
                    Case "<COLUMN SOURCE>"
                        column.ColumnName.Name = t.Text
                    Case "<TABLE ALIAS>"
                        column.ColumnName.TableAlias = t.Text
                    Case "<COLUMN ALIAS>"
                        column.ColumnAlias = t.Text
                    Case "<AGGREGATE>"
                        If t.Text <> "(" AndAlso t.Text <> ")" Then
                            column.Aggregate = ConvertFunctionName(t.Text)
                        End If
                    Case "<VALUE>"
                        column.ColumnName.Name = t.Text
                End Select
        End Select
        Return column
    End Function

    Private Function ConvertFunctionName(ByVal SQLFunctionName As String) As String
        Select Case SQLFunctionName.ToUpper()
            Case "AVG"
                Return "AVG"
            Case "SUM"
                Return "SUM"
            Case "COUNT"
                Return "COUNT"
            Case "MAX"
                Return "MAX"
            Case "MIN"
                Return "MIN"
            Case "STDEV"
                Return "STDEV"
            Case "VAR"
                Return "VAR"
            Case Else
                Return SQLFunctionName
        End Select
    End Function
    Private Function ParseFromClause(ByVal node As Parser.SyntaxNode, ByVal rulename As String, ByVal clause As FromClause) As FromClause
        Debug.Assert(node IsNot Nothing)
        Debug.Assert(node.GetType.ToString() = GetType(Parser.NonTerminalNode).ToString)

        Dim root As Parser.NonTerminalNode = CType(node, Parser.NonTerminalNode)
        If clause Is Nothing Then clause = New FromClause

        For idx As Integer = 0 To root.Count - 1
            Select Case root(idx).GetType().ToString
                Case GetType(Parser.NonTerminalNode).ToString
                    Dim n As Parser.NonTerminalNode = CType(root(idx), Parser.NonTerminalNode)
                    rulename = n.RuleName
                    Select Case rulename.ToUpper
                        Case "<FROM CLAUSE>"
                            clause = New FromClause
                        Case "<FROM ID LIST>"
                            clause.Tables.Add(ParseFromTables(n, rulename, New Table))
                        Case "<ID MEMBER>"
                            clause.Tables.Add(ParseFromTables(n, rulename, New Table))
                    End Select
                Case GetType(Parser.TerminalNode).ToString
                    Dim t As Parser.TerminalNode = CType(root(idx), Parser.TerminalNode)
                    Select Case rulename.ToUpper
                        Case "<JOIN CHAIN>"
                            Console.WriteLine("JOIN CHAIN")
                    End Select
            End Select
        Next
        Return clause
    End Function

    Private Function ParseFromTables(ByVal node As Parser.SyntaxNode, ByVal ruleName As String, ByVal table As Table) As Table
        Select Case node.GetType().ToString
            Case GetType(Parser.NonTerminalNode).ToString
                Dim n As Parser.NonTerminalNode = CType(node, Parser.NonTerminalNode)
                ruleName = n.RuleName
                For idx As Integer = 0 To n.Count - 1
                    ParseFromTables(n.Item(idx), ruleName, table)
                Next
            Case GetType(Parser.TerminalNode).ToString
                Dim t As Parser.TerminalNode = CType(node, Parser.TerminalNode)
                Select Case ruleName.ToUpper
                    Case "<VIEW ID>"
                        If t.Text <> "VIEW" Then
                            table.View = t.Text
                        End If
                    Case "<ID MEMBER>"
                        table.List = t.Text
                    Case "<ID ALIAS>"
                        table.Alias = t.Text
                End Select
        End Select
        Return table
    End Function

    Private Function ParseWhereClause(ByVal node As Parser.SyntaxNode, ByVal ruleName As String, ByVal clause As WhereClause) As WhereClause
        Debug.Assert(node IsNot Nothing)
        Debug.Assert(node.GetType.ToString = GetType(Parser.NonTerminalNode).ToString)
        Dim root As Parser.NonTerminalNode = CType(node, Parser.NonTerminalNode)
        If clause Is Nothing Then clause = New WhereClause
        For idx As Integer = 0 To root.Count - 1
            Select Case root(idx).GetType().ToString
                Case GetType(Parser.NonTerminalNode).ToString
                    Dim n As Parser.NonTerminalNode = CType(root(idx), Parser.NonTerminalNode)
                    ruleName = n.RuleName
                    Select Case ruleName.ToUpper
                        Case "<EXPRESSION>"
                            clause.Expression = ParseExpression(n, n.RuleName, clause.Expression)
                        Case "<TUPLE>"
                            clause.Expression = ParseExpression(n, ruleName, New LogicalExpression)
                        Case "<PRED EXP>"
                            clause.Expression = ParsePredicateExpression(n, ruleName, New PredicateExpression, False)
                        Case Else
                            '                        For idx As Integer = 0 To n.Count - 1
                            'ParseWhereClause(n.Item(idx), ruleName, clause, ProcessLeft)
                            'Next
                    End Select

                Case GetType(Parser.TerminalNode).ToString
                    Dim t As Parser.TerminalNode = CType(root(idx), Parser.TerminalNode)
                    Select Case ruleName.ToUpper

                    End Select
            End Select
        Next
        Return clause
    End Function

    Private Function ParseTurple(ByVal node As Parser.SyntaxNode, ByVal ruleName As String, ByVal expression As BaseExpression) As BaseExpression
        Debug.Assert(node IsNot Nothing)
        Debug.Assert(node.GetType.ToString = GetType(Parser.NonTerminalNode).ToString)
        Dim root As Parser.NonTerminalNode = CType(node, Parser.NonTerminalNode)
        Dim processed As Boolean = False
        For idx As Integer = 0 To root.Count - 1
            Select Case root.Item(idx).GetType().ToString
                Case GetType(Parser.NonTerminalNode).ToString
                    Dim n As Parser.NonTerminalNode = CType(root.Item(idx), Parser.NonTerminalNode)
                    Select Case n.RuleName.ToUpper
                        Case "<TUPLE>"
                            expression = ParseTurple(n, n.RuleName, expression)
                        Case "<EXPRESSION>"
                            If expression Is Nothing Then expression = New LogicalExpression
                            expression = ParseExpression(n, n.RuleName, expression)
                        Case "<PRED EXP>"
                            expression = ParsePredicateExpression(n, n.RuleName, expression, False)
                    End Select
                Case GetType(Parser.TerminalNode).ToString
            End Select
        Next
        Return expression
    End Function

    Private Function ParseExpression(ByVal node As Parser.SyntaxNode, ByVal ruleName As String, ByVal expression As LogicalExpression) As LogicalExpression
        Debug.Assert(node IsNot Nothing)
        Debug.Assert(node.GetType.ToString = GetType(Parser.NonTerminalNode).ToString)
        Dim root As Parser.NonTerminalNode = CType(node, Parser.NonTerminalNode)
        Dim processed As Boolean = False

        If expression Is Nothing Then expression = New LogicalExpression
        For idx As Integer = 0 To root.Count - 1
            Select Case root.Item(idx).GetType().ToString
                Case GetType(Parser.NonTerminalNode).ToString
                    Dim n As Parser.NonTerminalNode = CType(root.Item(idx), Parser.NonTerminalNode)
                    Select Case n.RuleName.ToUpper
                        Case "<TUPLE>"
                            If expression.Op = BooleanOperators.None Then
                                expression.Left = ParseTurple(n, n.RuleName, expression.Left)
                            Else
                                expression.Right = ParseTurple(n, n.RuleName, expression.Right)
                            End If
                        Case "<EXPRESSION>"
                            If expression.Op = BooleanOperators.None Then
                                expression.Left = ParseExpression(n, n.RuleName, expression.Left)
                            Else
                                expression.Right = ParseExpression(n, n.RuleName, expression.Right)
                            End If
                        Case "<PRED EXP>"
                            expression.Left = ParsePredicateExpression(n, n.RuleName, expression.Left, False)
                    End Select
                Case GetType(Parser.TerminalNode).ToString
                    Dim t As Parser.TerminalNode = CType(root.Item(idx), Parser.TerminalNode)
                    Select Case ruleName.ToUpper
                        Case "<EXPRESSION>"
                            Select Case t.Text.ToUpper
                                Case "OR"
                                    expression.Op = BooleanOperators.Or
                                Case "AND"
                                    expression.Op = BooleanOperators.And
                            End Select
                    End Select
            End Select
        Next
        Return expression
    End Function

    Private Function ParsePredicateExpression(ByVal node As Parser.SyntaxNode, ByVal ruleName As String, ByVal expression As PredicateExpression, ByRef ProcessLeft As Boolean) As PredicateExpression

        Debug.Assert(node.GetType.ToString() = GetType(Parser.NonTerminalNode).ToString)

        If expression Is Nothing Then expression = New PredicateExpression

        Dim root As Parser.NonTerminalNode = CType(node, Parser.NonTerminalNode)
        For idx As Integer = 0 To root.Count - 1
            Select Case root.Item(idx).GetType().ToString
                Case GetType(Parser.NonTerminalNode).ToString
                    Dim n As Parser.NonTerminalNode = root.Item(idx)
                    ParsePredicateExpression(n, n.RuleName, expression, ProcessLeft)
                Case GetType(Parser.TerminalNode).ToString
                    Dim t As Parser.TerminalNode = root.Item(idx)
                    Select Case root.RuleName.ToUpper
                        Case "<VALUE>"
                            If ProcessLeft = False Then
                                expression.Left = t.Text
                                ProcessLeft = True
                            Else
                                expression.Right = t.Text
                            End If
                        Case "<PARAMETER NAME>"
                            If ProcessLeft = False Then
                                expression.Left = "@" & t.Text
                                ProcessLeft = True
                            Else
                                expression.Right = "@" & t.Text
                            End If
                        Case "<PRED EXP>"
                            Select Case t.Text
                                Case "="
                                    expression.Op = Operators.Equals
                            End Select
                    End Select
            End Select
        Next
        Return expression
    End Function

    Private Function ParseOrderClause(ByVal node As Parser.SyntaxNode, ByVal tables As List(Of Table), ByVal ruleName As String, ByVal clause As OrderClause) As OrderClause
        Debug.Assert(node IsNot Nothing)
        Debug.Assert(node.GetType.ToString = GetType(Parser.NonTerminalNode).ToString)
        Dim root As Parser.NonTerminalNode = CType(node, Parser.NonTerminalNode)
        Dim processed As Boolean = False

        Dim order As New OrderType
        If clause Is Nothing Then clause = New OrderClause
        For idx As Integer = 0 To root.Count - 1
            Select Case root.Item(idx).GetType().ToString
                Case GetType(Parser.NonTerminalNode).ToString
                    Dim n As Parser.NonTerminalNode = CType(root.Item(idx), Parser.NonTerminalNode)
                    ruleName = n.RuleName
                    Select Case n.RuleName.ToUpper
                        Case "<ORDER LIST>"
                            clause = ParseOrderClause(n, tables, ruleName, clause)
                        Case "<ORDER TYPE>"
                            order = ParseOrderType(n, ruleName, order)
                    End Select
                Case GetType(Parser.TerminalNode).ToString
                    Dim t As Parser.TerminalNode = CType(root.Item(idx), Parser.TerminalNode)
                    Console.WriteLine(t.Text)
                    Select Case ruleName.ToUpper
                        Case "<ORDER LIST>"
                            'HACK: Do this better
                            If t.Text.Contains(".") Then
                                Dim table As String = t.Text.Split(".")(0)
                                Dim column As String = t.Text.Split(".")(1)
                                order.Column = column
                                For Each id As Table In tables
                                    If id.Alias = table Then
                                        order.Column = column
                                    End If
                                Next
                            Else
                                order.Column = t.Text
                            End If
                            clause.List.Add(order)
                    End Select
            End Select
        Next
        Return clause
    End Function

    Private Function ParseOrderType(ByVal node As Parser.SyntaxNode, ByVal ruleName As String, ByVal order As OrderType) As OrderType

        Debug.Assert(node IsNot Nothing)
        Debug.Assert(node.GetType.ToString = GetType(Parser.NonTerminalNode).ToString)
        Dim root As Parser.NonTerminalNode = CType(node, Parser.NonTerminalNode)
        Dim processed As Boolean = False

        If order Is Nothing Then order = New OrderType

        For idx As Integer = 0 To root.Count - 1
            Select Case root.Item(idx).GetType().ToString
                Case GetType(Parser.NonTerminalNode).ToString
                    Dim n As Parser.NonTerminalNode = CType(root.Item(idx), Parser.NonTerminalNode)
                    ruleName = n.RuleName
                Case GetType(Parser.TerminalNode).ToString
                    Dim t As Parser.TerminalNode = CType(root.Item(idx), Parser.TerminalNode)
                    Console.WriteLine(t.Text)
                    Select Case ruleName.ToUpper
                        Case "<ORDER TYPE>"
                            If t.Text = "DESC" Then
                                order.Ascending = False
                            Else
                                order.Ascending = True
                            End If
                    End Select
            End Select
        Next
        Return order
    End Function

    Private Function ParseTopClause(ByVal root As Parser.NonTerminalNode, ByVal ruleName As String) As Integer

        For idx As Integer = 0 To root.Count - 1
            Dim node As Parser.SyntaxNode = root.Item(idx)
            Select Case node.GetType().ToString
                Case GetType(Parser.TerminalNode).ToString
                    Dim t As Parser.TerminalNode = CType(node, Parser.TerminalNode)
                    Select Case t.Text.ToUpper
                        Case "TOP"
                        Case Else
                            Return t.Text
                    End Select
            End Select
        Next
    End Function

    Private Function ParseGroupClause(ByVal node As Parser.SyntaxNode, ByVal ruleName As String, ByVal clause As GroupByClause) As GroupByClause
        Debug.Assert(node IsNot Nothing)
        Debug.Assert(node.GetType.ToString = GetType(Parser.NonTerminalNode).ToString)
        Dim root As Parser.NonTerminalNode = CType(node, Parser.NonTerminalNode)
        Dim processed As Boolean = False
        Dim order As New OrderType
        If clause Is Nothing Then clause = New GroupByClause
        For idx As Integer = 0 To root.Count - 1
            Select Case root(idx).GetType().ToString
                Case GetType(Parser.NonTerminalNode).ToString
                    Dim n As Parser.NonTerminalNode = CType(root.Item(idx), Parser.NonTerminalNode)
                    Select Case n.RuleName.ToUpper
                        Case "<ID LIST>", "<ID MEMBER>"
                            clause = ParseGroupClause(n, n.RuleName, clause)
                    End Select
                Case GetType(Parser.TerminalNode).ToString
                    Dim t As Parser.TerminalNode = CType(root(idx), Parser.TerminalNode)
                    Select Case ruleName.ToUpper
                        Case "<ID MEMBER>"
                            clause.List.Add(t.Text)
                    End Select
            End Select
        Next
        Return clause
    End Function
    Private Sub Parse(ByVal noder As Parser.SyntaxNode, ByVal ruleName As String, ByVal level As Integer)
        Debug.Assert(noder IsNot Nothing)
        Debug.Assert(noder.GetType.ToString = GetType(Parser.NonTerminalNode).ToString)
        Dim root As Parser.NonTerminalNode = CType(noder, Parser.NonTerminalNode)

        For idx As Integer = 0 To root.Count - 1
            Dim node As Parser.SyntaxNode = root.Item(idx)
            Select Case node.GetType().ToString
                Case GetType(Parser.NonTerminalNode).ToString
                    Dim n As Parser.NonTerminalNode = node
                    ruleName = n.RuleName
                    Console.WriteLine(Space(level) & ruleName)
                    Select Case n.RuleName.ToUpper()
                        Case "<RESTRICTION>"

                        Case "<TOP CLAUSE>"
                            Me.RowLimit = ParseTopClause(n, ruleName)
                        Case "<COLUMN ITEM>"
                            Me.Columns.Add(ParseColumn(n, n.RuleName, Nothing))
                        Case "<COLUMNS>"
                            Me.Columns = ParseColumns(node, ruleName, Nothing)
                        Case "<COLUMN LIST>"
                            Me.Columns = ParseColumnList(node, ruleName, Nothing)
                        Case "<FROM CLAUSE>"
                            Me.FromClause = ParseFromClause(node, ruleName, Nothing)
                        Case "<WHERE CLAUSE>"
                            Me.WhereClause = ParseWhereClause(node, ruleName, Nothing)
                        Case "<ORDER CLAUSE>"
                            Me.OrderClause = ParseOrderClause(node, Me.FromClause.Tables, ruleName, Nothing)
                        Case "<GROUP CLAUSE>"
                            Me.GroupClause = ParseGroupClause(node, ruleName, Nothing)
                            'Case Else
                            '    For idx As Integer = 0 To n.Count - 1
                            '        Parse(n.Item(idx), n.Rule.Name, level + 1)
                            '    Next
                    End Select
                Case GetType(Parser.TerminalNode).ToString
                    Dim t As Parser.TerminalNode = CType(node, Parser.TerminalNode)
                    Console.WriteLine(Space(level) & t.Text)
                  
            End Select
        Next
    End Sub

End Class






